<?php
/**
 *  NevuxBB - Free Forum Software
 *  Copyright (C) 2008, 2010 NevuxBB (No one specific author)*  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Extra info: The protocol and all Google data recieved through this
 *              protocol are all licensed to Google including but not
 *				not limited to the Google Name, Logo and the Google Safe
 *				Browsing Lists. These lists are provided for free by Google.
 *
 * Google Safe Browsing API Controller
 * File: safebrowsing_class.php
 * Purpose: For making sure URL's are safe
 * Todo: Perform tests for security and peformance - make sure it works first
 *
 */

class safeBrowsing {
	private $key;
	private $black_list;
	private $black_list_status;
	private $malware_list;
	private $malware_list_status;
	private $final_black;
	private $final_malware;
	private $extra_info;
	
	/**
	* Used to set GSB settings
	*
	* @param string $information
	* @return void
	* @access public
	*/	
	public function insertInfo($information) {
		$this->key = $information['google_key'];
		$this->black_list_status = $information['bls'];
		$this->malware_list_status = $information['mls'];
	}
	
	/**
	* Used to check the lists check out
	*
	* @param string $list
	* @return boolean
	* @access private
	*/	
	private function isReal($list) {
		// is it black list?
		if($list == 'black') {
			// make sure it didn't return a 404
			if(preg_match('/<title>403 Forbidden</title>/', $this->black_list)) {
				// black list isn't valid
				return false;
			} else {
				// didn't find a forbidden sign
				return true;
			}
		}
		
		// is it malware list?
		if($list == 'malware') {
			// now check the malware result
			if(preg_match('/<title>403 Forbidden</title>/', $this->malware_list)) {
				// malware list isn't valid
				return false;
			} else {
				// malware list IS valid
				return true;
			}
		}
	}
	
	/**
	* Used to update the lists
	*
	* @return string or boolean
	* @access public
	*/	
	public function upadteLists() {
		// requires no special paramaters
		$black_list_num = GlobalSettings::getSetting('google_safebrowsing_blacklist_version');
		$malware_list_num = GlobalSettings::getSetting('google_safebrowsing_malware_version');
		
		// blacklist url
		$black_url   = 'http://sb.google.com/safebrowsing/update?client=api&apikey=' . $this->key . '&version=goog-black-hash:%s';
		$malware_url = 'http://sb.google.com/safebrowsing/update?client=api&apikey=' . $this->key . '&version=goog-malware-hash:%s';
		
		// start testing and downloading
		if($black_list_num == (NULL || '')) {
			if(extension_loaded('curl')) {
				// we can use curl
				$cu = curl_init(sprintf($black_url, '1:-1'));
				
				// set curl options
				curl_setopt($cu, CURLOPT_HEADER, 0);
				curl_setopt($cu, CURLOPT_POST, 1);
				curl_setopt($cu, CURLOPT_RETURNTRANSFER, 1);
				
				// update the class
				$this->black_list = curl_exec($cu);
				
				// close
				curl_close($cu);
			} elseif(ini_get('allow_url_fopen')) {
				// we have to use file_get_contents
				$this->black_list = file_get_contents(sprintf($black_url, '1:-1'));
			} else {
				// we can't obtain these lists
				return false;
			}
		} else {
			// we need to upgrade this lists
			if(extension_loaded('curl')) {
				// we can use curl
				$cu = curl_init(sprintf($black_url, $this->black_list_status));
				
				// set curl options
				curl_setopt($cu, CURLOPT_HEADER, 0);
				curl_setopt($cu, CURLOPT_POST, 1);
				curl_setopt($cu, CURLOPT_RETURNTRANSFER, 1);
				
				// update the class
				$this->black_list = curl_exec($cu);
				
				// close
				curl_close($cu);
			} elseif(ini_get('allow_url_fopen')) {
				// we have to use file_get_contents
				$this->black_list = file_get_contents(sprintf($black_url, $this->black_list_status));
			} else {
				// we can't obtain these lists
				return false;
			}
		}
		
		// now test and download the malware links
		if($malware_list_num == (NULL || '')) {
			if(extension_loaded('curl')) {
				// we can use curl
				$cu = curl_init(sprintf($malware_url, '1:-1'));
				
				// set curl options
				curl_setopt($cu, CURLOPT_HEADER, 0);
				curl_setopt($cu, CURLOPT_POST, 1);
				curl_setopt($cu, CURLOPT_RETURNTRANSFER, 1);
				
				// update the class
				$this->malware_list = curl_exec($cu);
				
				// close
				curl_close($cu);
			} elseif(ini_get('allow_url_fopen')) {
				// we have to use file_get_contents
				$this->malware_list = file_get_contents(sprintf($malware_url, '1:-1'));
			} else {
				// we can't obtain these lists
				return false;
			}
		} else {
			// we need to upgrade this lists
			if(extension_loaded('curl')) {
				// we can use curl
				$cu = curl_init(sprintf($malware_url, $this->malware_list_status));
				
				// set curl options
				curl_setopt($cu, CURLOPT_HEADER, 0);
				curl_setopt($cu, CURLOPT_POST, 1);
				curl_setopt($cu, CURLOPT_RETURNTRANSFER, 1);
				
				// update the class
				$this->malware_list = curl_exec($cu);
				
				// close
				curl_close($cu);
			} elseif(ini_get('allow_url_fopen')) {
				// we have to use file_get_contents
				$this->malware_list = file_get_contents(sprintf($malware_url, $this->malware_list_status));
			} else {
				// we can't obtain these lists
				return false;
			}
		}
	}
	
	/**
	* Used to change a link
	*
	* @param string $link
	* @param string $type
	* @param string $action
	* @return void
	* @access private
	*/	
	private function modifyLink($link, $type, $action) {
		if($action == 1) {
			// insert a new link into the database
			Db_Abstraction::query("INSERT INTO `nbb_gsb` (`gsb_link`, `gsb_list`) VALUES ('" . trim($link) . "', '" . $type . "')");
		} elseif($action == 0) {
			// remove a link
			Db_Abstraction::query("DELETE FROM `nbb_gsb` WHERE `gsb_link` = '" . trim($link) . "' AND `gsb_list` = '" . $type . "'LIMIT 1")
		} else {
			// not right, break
			break;
		}
	}
	
	/**
	* Used to parse the links into an easier format
	*
	* @return void
	* @access private
	*/	
	public function parseLists() {
		// lets start with the black list
		$this->black_list = explode("\n", $this->black_list);
		
		if(self::isReal('black')) {
			// begin processing
			foreach($this->black_list as $object) {
				if($object[0] == '[') {
					// this is an information line
					$replace = array('[' => '', ']' => '');
					$new_obj = strtr($object, $replace);
					
					// seperate each piece out
					$new_obj = explode(' ', $new_obj);
					
					if($new_obj[0] != 'goog-black-hash') {
						// something went wrong
						$this->final_black = '3';
					} else {
						// seems to be correct
						// is this an update?
						if($new_obj[2] == 'update') {
							// is an update
							// does it match the current numbers?
							$this_num = substr($new_obj[1], '.', ':');
							
							if($this_num == $this->black_list_status) {
								// already up to date
								$this->final_black = '6';
							} else {
								// out of date or do not match, let's update
								$this->extra_info['black_list']['new_version'] = $this_num;
							}
						} else {
							// is a new pack
							$this_num = substr($new_obj[1], '.', ':');
							$this->extra_info['black_list']['new_version'] = $this_num;
						}
					}
				} else {
					// isn't an object line
					if($this->final_black === NULL) {
						// proceed as we haven't hit a error yet
						if($object[0] == '+') {
							// we need to add a new URL
							self::modifyLink(substr($object, 1), 'black', 1);
						} elseif($object[0] == '-') {
							// we need to remove the URL
							self::modifyLink(substr($object, 1), 'black', 0);
						} else {
							// this is a blank line
							break;
						}
					} else {
						// this list failed to complete, skip
						break;
					}
				}
			}
		} else {
			// isn't real, turns out, it's a 404 error
			$this->final_black = '4';
		}
		
		// test the malware list
		$this->malware_list = explode("\n", $this->malware_list);
		
		if(self::isReal('malware')) {
			// begin processing
			foreach($this->malware_list as $object) {
				if($object[0] == '[') {
					// this is an information line
					$replace = array('[' => '', ']' => '');
					$new_obj = strtr($object, $replace);
					
					// seperate each piece out
					$new_obj = explode(' ', $new_obj);
					
					if($new_obj[0] != 'goog-malware-hash') {
						// something went wrong
						$this->final_malware = '3';
					} else {
						// seems to be correct
						// is this an update?
						if($new_obj[2] == 'update') {
							// is an update
							// does it match the current numbers?
							$this_num = substr($new_obj[1], '.', ':');
							
							if($this_num == $this->malware_list_status) {
								// already up to date
								$this->final_malware = '6';
							} else {
								// out of date or do not match, let's update
								$this->extra_info['malware_list']['new_version'] = $this_num;
							}
						} else {
							// is a new pack
							$this_num = substr($new_obj[1], '.', ':');
							$this->extra_info['malware_list']['new_version'] = $this_num;
						}
					}
				} else {
					// isn't an object line
					if($this->final_malware === NULL) {
						// proceed as we haven't hit a error yet
						if($object[0] == '+') {
							// we need to add a new URL
							self::modifyLink(substr($object, 1), 'malware', 1);
						} elseif($object[0] == '-') {
							// we need to remove the URL
							self::modifyLink(substr($object, 1), 'malware', 0);
						} else {
							// this is a blank line
							break;
						}
					} else {
						// this list failed to complete, skip
						break;
					}
				}
			}
		} else {
			// isn't real, turns out, it's a 404 error
			$this->malware_black = '4';
		}
	}
}

?>